
#include <stdio.h>
#include <math.h>
#include <ctime>
#include <string>
#include "data.c"
#include <sys/timeb.h>
#include <stdlib.h>
#include <string.h>
using namespace std;

int WIDTH = 10;
int HEIGHT = 10;


#ifdef WIN32
  #define STRICT
  #define WIN32_LEAN_AND_MEAN
  #pragma warning( disable : 4786 ) //MSVC annoyance
  #pragma warning( disable : 4305 ) //MSVC annoyance
  //#include <windows.h>
  //#include <scrnsave.h>
  #include "GLScreenSaver.hpp"
  #include <GL/gl.h>
  #include "GL/glu.h"

const int UPDATE_INTERVAL = 5000; //5000 milliseconds = 5 seconds
const int UPDATE_TIMER = 2;

	//screensaver class
	GLScreenSaver scr;
  /*#include "wglext.h"

  //#define WGL_SAMPLE_BUFFERS_ARB  0x2041
  //#define WGL_SAMPLES_ARB    0x2042

  HWND  g_hWnd = NULL;
  HDC    g_hDC = NULL;
  HGLRC g_hRC  = NULL;
  HINSTANCE g_hInstance = NULL;

  int g_VideoMode = 1;
  int g_ColorDepth = 32;
  int g_Multisampling = 1;
  int g_TextureFilter = 1;
  int g_VSync = 1;
  float VNEAR = -1000.0f;
  float VFAR = 1000.0f;
  bool arbMultisampleSupported = false; //default unsupported
  int PixelFormat;
  */
  GLuint font_base;
#endif

#ifdef LINUX
  #include <unistd.h>
  #include <X11/keysym.h>
  #include <X11/Xlib.h>
  #include <X11/Xutil.h>
  #include <GL/gl.h>
  #include <GL/glx.h>
  
  Display *dpy;
  Window glwin;
  static int attributes[] = {GLX_DEPTH_SIZE, 16, GLX_RGBA, GLX_RED_SIZE, 4, GLX_GREEN_SIZE, 4, GLX_BLUE_SIZE, 4, GLX_DOUBLEBUFFER, None};
  
  #ifdef FREETYPE
    #include <FTGL/FTGLTextureFont.h>
    #include "font.c"
    FTGLTextureFont *font;
  #endif
#endif


const char * wday_names[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

char displaymode = 'r';
float aspect;
int x_offset = 0;
int y_offset = 0;
float now = 0;
int now_hour = 0;
int now_min = 0;
int now_sec = 0;
float speed = 9;
bool all_trips_generated = false;
float zoom = 1;
int num_buses = 0;
struct tm * timeinfo;
GLuint shape_dl;

int num_loaded_schedules = 0;


struct sStopTime {
public:
  int arrival;
  int departure;
  int shape_position;
  int noun_id;
  float x;
  float y;
};

class cTrip {
public:
  ~cTrip();
  int shape_id;
  int start;
  int end;
  int num_stop_times;
  sStopTime * stop_times;
};

cTrip::~cTrip()
{
  if (num_stop_times > 0) free(stop_times);
}

class cService {
public:
  cService();
  ~cService();
  int start;
  int end;
  int num_trips;
  cTrip * trips;
};

cService::cService()
{
  num_trips = 0;
  start = 100000000;
  end = -100000000;
}

cService::~cService()
{
  if (num_trips > 0) free(trips);
}

cService services[3];

inline void drawCircle(int x, int y, float r)
{
  glTexCoord2f(0.0f, 1.0f); glVertex3f(y-r, x+r, 0);
  glTexCoord2f(1.0f, 1.0f); glVertex3f(y+r, x+r, 0);
  glTexCoord2f(1.0f, 0.0f); glVertex3f(y+r, x-r, 0);
  glTexCoord2f(0.0f, 0.0f); glVertex3f(y-r, x-r, 0);
}

inline void drawBox(float x, float y, float angle, float length=10, float width=8)
{
  width = width / 2;
  length = length / 2;
  glVertex3f((y+cos(angle+1.5707963)*width-cos(angle)*length), (x+sin(angle+1.5707963)*width-sin(angle)*length), 0);
  glVertex3f((y+cos(angle-1.5707963)*width-cos(angle)*length), (x+sin(angle-1.5707963)*width-sin(angle)*length), 0);
  glVertex3f((y+cos(angle-1.5707963)*width*.5+cos(angle)*length), (x+sin(angle-1.5707963)*width*.5+sin(angle)*length), 0);
  glVertex3f((y+cos(angle+1.5707963)*width*.5+cos(angle)*length), (x+sin(angle+1.5707963)*width*.5+sin(angle)*length), 0);
}

#ifdef WIN32

  GLvoid BuildFont(GLvoid)								// Build Our Bitmap Font
  {
	  HFONT	font;										// Windows Font ID
	  HFONT	oldfont;									// Used For Good House Keeping

	  font_base = glGenLists(96);								// Storage For 96 Characters

	  font = CreateFont(	-24,							// Height Of Font
						  0,								// Width Of Font
						  0,								// Angle Of Escapement
						  0,								// Orientation Angle
						  FW_BOLD,						// Font Weight
						  FALSE,							// Italic
						  FALSE,							// Underline
						  FALSE,							// Strikeout
						  ANSI_CHARSET,					// Character Set Identifier
						  OUT_TT_PRECIS,					// Output Precision
						  CLIP_DEFAULT_PRECIS,			// Clipping Precision
						  ANTIALIASED_QUALITY,			// Output Quality
						  FF_DONTCARE|DEFAULT_PITCH,		// Family And Pitch
						  "Garamond");					// Font Name

	  oldfont = (HFONT)SelectObject(GetDC(GLScreenSaver::hWnd), font);           // Selects The Font We Want
	  wglUseFontBitmaps(GetDC(GLScreenSaver::hWnd), 32, 96, font_base);				// Builds 96 Characters Starting At Character 32
	  SelectObject(GetDC(GLScreenSaver::hWnd), oldfont);							// Selects The Font We Want
	  DeleteObject(font);									// Delete The Font
  }

  GLvoid KillFont(GLvoid)									// Delete The Font List
  {
	  glDeleteLists(font_base, 96);							// Delete All 96 Characters
  }

  GLvoid glPrint(const char *fmt, ...)					// Custom GL "Print" Routine
  {
	  char text[256];								// Holds Our String
	  va_list ap;										// Pointer To List Of Arguments

	  if (fmt == NULL)									// If There's No Text
		  return;											// Do Nothing

	  va_start(ap, fmt);									// Parses The String For Variables
	      vsprintf(text, fmt, ap);						// And Converts Symbols To Actual Numbers
	  va_end(ap);											// Results Are Stored In Text

	  glPushAttrib(GL_LIST_BIT);							// Pushes The Display List Bits
	  glListBase(font_base - 32);								// Sets The Base Character to 32
	  glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);	// Draws The Display List Text
	  glPopAttrib();										// Pops The Display List Bits
  }

#endif

void generate_trips(int s)
{
  
  num_loaded_schedules ++;
  schedules[s].trips_generated = 1;

  if (schedules[s].service_id > 3) { printf("dah\n"); exit(1); }
  if (schedules[s].start_time < schedules[s].end_time && schedules[s].start_time != 0 && schedules[s].end_time != 0 && schedules[s].shape_id != 0)
  {
    cService * service = &services[schedules[s].service_id-1];
    int shape_size = (int)shapes[schedules[s].shape_id].size;

    if (shape_size == 0)
    {
      char fefe[60];
      sprintf(fefe, " s:%d shape_id:%d shape_size:%d service_id:%d ", s, schedules[s].shape_id, shape_size, schedules[s].service_id);
      //MessageBox(g_hWnd, fefe, "fefe", MB_OK);
      num_loaded_schedules ++;
      schedules[s].trips_generated = 1;
      return;
    }

    for (int i = schedules[s].start_time ; i < schedules[s].end_time ; i += schedules[s].headway)
    {
      
      int trip_id = service->num_trips;
      service->num_trips++;
      
      service->trips = (cTrip*)realloc(service->trips, sizeof(cTrip)*(service->num_trips+1));
      
      cTrip * trip = &service->trips[trip_id];
      
      trip->shape_id = schedules[s].shape_id;
      trip->start = i;
      
      trip->num_stop_times = 0;
	    int point_index = 0;
      
      for (point_index = 0 ; point_index < shape_size ; point_index++)
      {
        if (schedules[s].shape_id == 0) continue;
        if ((shapes[schedules[s].shape_id].points + point_index)->noun_id == 0) continue;
        trip->num_stop_times ++;
      }
      
      if (trip->num_stop_times == 0) return;

      trip->end = i + (int)(schedules[s].travel_time);// - (int)(trip->num_stop_times*9); // 9 sec pause for each stop
      
      trip->stop_times = (sStopTime*)malloc(sizeof(sStopTime)*(trip->num_stop_times+1));
      
      sStopTime * stop_time = trip->stop_times;
      
      int prev_departure = 0;
      for (point_index = 0 ; point_index < shape_size ; point_index++)
      {
        if ((shapes[schedules[s].shape_id].points + point_index)->noun_id == 0) continue;

        stop_time->arrival = (int)trip->start + ((shapes[schedules[s].shape_id].points + point_index)->distance / (shapes[schedules[s].shape_id].points + shape_size - 1)->distance) * (trip->end - trip->start);
		if (stop_time->arrival < prev_departure) stop_time->arrival = prev_departure + 1;
        stop_time->departure = stop_time->arrival + 9;
        stop_time->noun_id = (shapes[schedules[s].shape_id].points + point_index)->noun_id;
        stop_time->x = (shapes[schedules[s].shape_id].points + point_index)->x;
        stop_time->y = (shapes[schedules[s].shape_id].points + point_index)->y;
        stop_time->shape_position = point_index;
		prev_departure = stop_time->departure;
        
        if (stop_time->departure > trip->end) trip->end = stop_time->departure;
        
        stop_time ++; // increment pointer to next stop_time
      }
    }
  }
}

GLvoid resize( GLsizei width, GLsizei height )
{
  glLoadIdentity();
  glViewport(0, 0, width, height);
  
  aspect = diff_lng / (float)diff_lat/1.6;
  
  if (diff_lat > diff_lng)
    glOrtho (center_lng+diff_lng/2/aspect, center_lng-diff_lng/2/aspect, center_lat-diff_lat/2, center_lat+diff_lat/2, -1000, 1000);
  else
    glOrtho (center_lng+diff_lng/2, center_lng-diff_lng/2, center_lat-diff_lat/2*aspect, center_lat+diff_lat/2*aspect, -1000, 1000);
}

GLvoid initializeGL()
{
  glEnable (GL_BLEND);
  glEnable (GL_TEXTURE_2D);
  glDisable (GL_DEPTH_TEST);
  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glEnable(GL_ALPHA_TEST);
  glAlphaFunc(GL_GEQUAL, 0.0625);
  glEnable(GL_POLYGON_OFFSET_LINE);
  glPolygonOffset(0.0, -3);
  #ifdef FREETYPE
    if (font != NULL) font->FaceSize((int)(0.05625*(double)WIDTH));
  #endif
  glClearColor(1,1,1,1);
  
  #ifdef WIN32
    BuildFont();
  #endif
}

void drawText(void)
{
  glEnable(GL_TEXTURE_2D);
  glPushMatrix();
    glLoadIdentity();
    glOrtho(0, WIDTH, 0, HEIGHT, -100, 100);
    glColor4f(0, 0, 0, .3);
    char fee[40];
    char fee2[40];
    strftime(fee2, 40, "%H:%M:%S", timeinfo);
    sprintf(fee, "(%s) %s", wday_names[timeinfo->tm_wday], fee2);
    glTranslatef(0.0350625*(double)WIDTH, HEIGHT-(0.1111111*(double)HEIGHT), 0);
    
    #ifdef WIN32
      glRasterPos2f(0,0);
      glPrint(fee);
    #elif FREETYPE
      glPushMatrix();
      if (font != NULL) font->Render(fee);
      glPopMatrix();
    #endif
    glTranslatef(0, -0.07*(double)HEIGHT, 0);
    glScalef(0.8, 0.8, 1);
    sprintf(fee, "Vehicles: %i", num_buses);
    #ifdef WIN32
      glRasterPos2f(0,0);
      glPrint(fee);
    #elif FREETYPE
      glPushMatrix();
      if (font != NULL) font->Render(fee);
      glPopMatrix();
    #endif
    if (!all_trips_generated)
    {
      glTranslatef(0, -0.07*(double)HEIGHT, 0);
      glScalef(0.8, 0.8, 1);
      sprintf(fee, "Loading ... (%d%%)", (int)(num_loaded_schedules / (float)num_schedules * 100));
      #ifdef WIN32
        glRasterPos2f(0,0);
        glPrint(fee);
      #elif FREETYPE
        glPushMatrix();
        if (font != NULL) font->Render(fee);
        glPopMatrix();
      #endif
    }
  glPopMatrix();
  glDisable(GL_TEXTURE_2D);
}

GLvoid Draw(void)
{
  timeb rawtime;
  ftime (&rawtime);
  timeinfo = localtime(&rawtime.time);
  
  if (displaymode == 'r')
  {
    now = timeinfo->tm_hour*3600 + timeinfo->tm_min*60 + timeinfo->tm_sec + ((float)rawtime.millitm/1000.0);
  }
  else if (displaymode == 'f')
  {
    now = now + speed;
    timeinfo->tm_year = timeinfo->tm_year;
    timeinfo->tm_mon = timeinfo->tm_mon;
    timeinfo->tm_mday = timeinfo->tm_mday;
    timeinfo->tm_hour = floor(now / 3600);
    
    timeinfo->tm_min = floor((now-timeinfo->tm_hour*3600) / 60);
    timeinfo->tm_sec = (int)now % 60;
    
    time_t temp = mktime(timeinfo);
    timeinfo = localtime(&temp);
  }
  
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  glPushMatrix();
  
  glTranslatef(center_lng, center_lat, 0);
  glScalef(zoom, zoom, 0);
  glTranslatef(-center_lng, -center_lat, 0);
  
  glTranslatef(x_offset, y_offset, 0);
  
  // draw shapes
  glDisable(GL_TEXTURE_2D);
  glColor4f(0.8, 0.8, 0.8, 0.9);
  glCallList(shape_dl);
  glEnable(GL_TEXTURE_2D);
  
  // draw buses
  num_buses = 0;
  glColor4f(0.8, 0.4, 0.4, 1.0);
  glDisable(GL_TEXTURE_2D);
  
  int y_wday = ((timeinfo->tm_wday - 1 < 0) ? wday_service_id[6] : wday_service_id[timeinfo->tm_wday-1]);
  int t_wday = ((timeinfo->tm_wday + 1 > 6) ? wday_service_id[0] : wday_service_id[timeinfo->tm_wday+1]);
  
  glBegin(GL_QUADS);
  int wdays[3] = {y_wday, wday_service_id[timeinfo->tm_wday], t_wday};
  
  int offset = -86400;
  
  for (int wday = 0 ; wday < 3 ; wday++)
  {
    for (int trip_id = 0 ; trip_id < services[wdays[wday]].num_trips ; trip_id ++)
    {
      bool drawn = false;
      cTrip * t = (services[wdays[wday]].trips + trip_id);
      if (t->start+offset > now || t->end+offset < now) continue;
      
      num_buses ++;
      
      if (t->shape_id == 0 || shapes[t->shape_id].size == 0) continue;
      
      sStopTime * prev_stop;
      sStopTime * dest_stop;
      
      for (int j = 0 ; j < t->num_stop_times - 1 ; j++)
      {
        prev_stop = t->stop_times+j;
        dest_stop = t->stop_times+j+1;
        
        if (dest_stop->arrival+offset <= now) continue; // already past or at the next stop
        
        if (prev_stop->departure+offset >= now) // hasn't left prev_stop yet
        {
          double ydiff = (shapes[t->shape_id].points + prev_stop->shape_position+1)->y - (shapes[t->shape_id].points + prev_stop->shape_position)->y;
          double xdiff = (shapes[t->shape_id].points + prev_stop->shape_position+1)->x - (shapes[t->shape_id].points + prev_stop->shape_position)->x;
          float angle = atan2(xdiff, ydiff);
          if (t->shape_id <= 8)
          {
            if (t->shape_id == 1 || t->shape_id == 2)      glColor4f(1.0, 198/255.0, 0.0, 1.0);
            else if (t->shape_id == 3 || t->shape_id == 4) glColor4f(44/255.0, 189/255.0, 0.0, 1.0);
            else if (t->shape_id == 5 || t->shape_id == 6) glColor4f(142/255.0, 0.0, 189/255.0, 1.0);
            else if (t->shape_id == 7 || t->shape_id == 8) glColor4f(0.0, 191/255.0, 203/255.0, 1.0);
            drawBox((shapes[t->shape_id].points + prev_stop->shape_position)->x, (shapes[t->shape_id].points + prev_stop->shape_position)->y, angle, 40, 15);
          }
          else
          {
            glColor4f(.8, .4, .4, 1);
            drawBox((shapes[t->shape_id].points + prev_stop->shape_position)->x, (shapes[t->shape_id].points + prev_stop->shape_position)->y, angle, 20, 10);
          }
          
          drawn = true;
          break;
        }
        
        unsigned int prev_time = prev_stop->departure+offset;
        unsigned int dest_time = dest_stop->arrival+offset;
        
        float prev_distance = (shapes[t->shape_id].points + prev_stop->shape_position)->distance;
        float dest_distance = (shapes[t->shape_id].points + dest_stop->shape_position)->distance;
        
        float between_stops_perc = (now - (float)prev_time) / (float)(dest_time - prev_time);
        
        float sin_between_stops_perc = (cos(3.1415926+between_stops_perc*3.1415926)+1) / 2;
        
        between_stops_perc = sin_between_stops_perc;
        
        if (between_stops_perc > 1 || between_stops_perc < 0) { printf("WTFBBQ!\n"); break; }
        
        float true_distance = (dest_distance - prev_distance) * between_stops_perc + prev_distance;
        
        for (int shape_position = prev_stop->shape_position ; shape_position < dest_stop->shape_position ; shape_position++)
        {
          if ((shapes[t->shape_id].points + shape_position)->distance <= true_distance && (shapes[t->shape_id].points + shape_position+1)->distance >= true_distance)
          {
            double ydiff = (shapes[t->shape_id].points + shape_position+1)->y - (shapes[t->shape_id].points + shape_position)->y;
            double xdiff = (shapes[t->shape_id].points + shape_position+1)->x - (shapes[t->shape_id].points + shape_position)->x;
            float angle = atan2(xdiff, ydiff);
            float dist = sqrt(ydiff*ydiff + xdiff*xdiff);
            float shapeperc = (true_distance - (shapes[t->shape_id].points + shape_position)->distance) / ((shapes[t->shape_id].points + shape_position+1)->distance - (shapes[t->shape_id].points + shape_position)->distance);
            
            float x = (shapes[t->shape_id].points + shape_position)->x + (sin(angle)*dist*shapeperc);
            float y = (shapes[t->shape_id].points + shape_position)->y + (cos(angle)*dist*shapeperc);
            
            if (t->shape_id <= 8)
            {
              if (t->shape_id == 1 || t->shape_id == 2)      glColor4f(1.0, 198/255.0, 0.0, 1.0);
              else if (t->shape_id == 3 || t->shape_id == 4) glColor4f(44/255.0, 189/255.0, 0.0, 1.0);
              else if (t->shape_id == 5 || t->shape_id == 6) glColor4f(142/255.0, 0.0, 189/255.0, 1.0);
              else if (t->shape_id == 7 || t->shape_id == 8) glColor4f(0.0, 191/255.0, 203/255.0, 1.0);
              drawBox(x, y, angle, 40, 15);
            }
            else
            {
              glColor4f(.8, .4, .4, 1);
              drawBox(x, y, angle, 20, 10);
            }
            
            drawn = true;
            break;
          }
        }
        
        break;
      }
    }
    offset += 86400;
  }
  
  glEnd();
  
  glPopMatrix();
  
  drawText();
  
  if (!all_trips_generated)
  {
    all_trips_generated = true;
    int num = 0;
    for (int s = 0 ; s < num_schedules ; s++)
    {
      if (schedules[s].trips_generated == 1) continue;
      generate_trips(s);
      num++;
      all_trips_generated = false;
      if (num > 500) break;
    }
  }
  
  #ifdef WIN32
    //Sleep(33);
    //SwapBuffers( g_hDC );
  #endif
  
  #ifdef LINUX
    usleep(33000);
    glXSwapBuffers(dpy, glwin);
  #endif
}

#ifdef WIN32

	void Setup(int w, int h)
	{
		char temp[100];
		GetPrivateProfileString("myttc", "mode", "realtime", temp, 100, "myttc.ini");
		if (strcmp(temp, "fastmode") == 0) displaymode = 'f';
		else displaymode = 'r';

		GetPrivateProfileString("myttc", "zoomlevel", "fullnetwork", temp, 100, "myttc.ini");
		if (strcmp(temp, "downtown") == 0)
		{
			zoom *= 4;
			y_offset += 750;
		}
		
		initializeGL();
		resize(w, h);

		glEnable(GL_TEXTURE_2D);
		glShadeModel(GL_SMOOTH);
		glClearDepth(1.0f);
		glDisable(GL_DEPTH_TEST);
		glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
		glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
		glEnable(GL_LINE_SMOOTH);
		shape_dl = glGenLists(1);
		glNewList(shape_dl, GL_COMPILE);
		for (int i = 0 ; i < num_shapes ; i++)
		{
			glBegin(GL_LINE_STRIP);
			for (int j = 0 ; j < shapes[i].size ; j++)
			glVertex3f((shapes[i].points + j)->y, (shapes[i].points + j)->x, 0);
			glEnd();
		}
		glEndList();
	}

	//handles timer calls
	void Timer(WPARAM wParam)
	{
		if (wParam == UPDATE_TIMER)
		{
			//curFIndex = random_number(0, 1);
			//curDrawIndex = random_number(0, 2);
		}
	}


	//clean up
	void Clean()
	{
		KillTimer(scr.hWnd, UPDATE_TIMER);
	}

	void Update()
	{
		
	}

	//entry point
	void screen_saver_main()
	{
		scr.glSetupFunc(Setup);
		scr.glUpdateFunc(Update);
		scr.glDrawFunc(Draw);
		scr.glTimerFunc(Timer);
		scr.glCleanFunc(Clean);

		SetTimer(scr.hWnd, UPDATE_TIMER, UPDATE_INTERVAL, NULL);
	}

  /*LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
  {
    switch( msg )
    {
      case WM_CHAR:
      {
        switch( wParam )
        {
          case '-':
            zoom /= 1.1;
          break;
          case '=':
            zoom *= 1.1;
          break;
        }
      }
      break;
      case WM_KEYDOWN:
      {
        switch( wParam )
        {
          case VK_UP:
            y_offset -= 50;
          break;
          case VK_DOWN:
            y_offset += 50;
          break;
          case VK_LEFT:
            x_offset -= 50;
          break;
          case VK_RIGHT:
            x_offset += 50;
          break;
          case 'F':
            displaymode = 'f';
          break;
          case 'R':
            displaymode = 'r';
          break;
          case 'Q':
          case VK_ESCAPE:
            PostQuitMessage(0);
          break;
        }
      }
      break;
  
      case WM_SIZE:
      {
        int nWidth  = LOWORD(lParam); 
        int nHeight = HIWORD(lParam);
        glViewport(0, 0, nWidth, nHeight);

        glMatrixMode( GL_PROJECTION );
        glLoadIdentity();
        gluPerspective( 45.0, (GLdouble)nWidth / (GLdouble)nHeight, VNEAR, VFAR);
      }
      break;

      case WM_CLOSE:
      {
        PostQuitMessage(0);  
      }

      case WM_DESTROY:
      {
        PostQuitMessage(0);
      }
      break;
    
      default:
      {
        return DefWindowProc( hWnd, msg, wParam, lParam );
      }
      break;
    }
    return 0;
  }*/

  enum TScrMode {smNone,smConfig,smPassword,smPreview,smSaver};
  TScrMode ScrMode=smNone;
  HINSTANCE hInstance=NULL;
  class TSaverSettings; TSaverSettings *ss=NULL;

  /*int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
  {
    WIDTH = GetSystemMetrics(SM_CXSCREEN);
    HEIGHT = GetSystemMetrics(SM_CYSCREEN);

    MSG uMsg;
    
    g_hInstance  = GetModuleHandle(NULL);

    WNDCLASS wc;
      wc.style      = CS_VREDRAW  | CS_HREDRAW | CS_SAVEBITS | CS_DBLCLKS;//CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
      wc.lpfnWndProc    = (WNDPROC)ScreenSaverProc;
      wc.cbClsExtra    = 0;
      wc.cbWndExtra    = 0;
      wc.hInstance    = g_hInstance;
      wc.hIcon      = LoadIcon(NULL, IDI_WINLOGO);
      wc.hCursor      = NULL;
      wc.hbrBackground  = (HBRUSH)GetStockObject(BLACK_BRUSH);
      wc.lpszMenuName    = NULL;
      wc.lpszClassName  = "WindowsScreenSaverClass";
    if (!RegisterClass(&wc))
    {
      MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
      return FALSE;
    }

    if(!CreateGLWindow()) PostQuitMessage(0);
    else init();
  
    do
    {
      if( PeekMessage( &uMsg, NULL, 0, 0, PM_REMOVE ) )
      {
        TranslateMessage( &uMsg );
        DispatchMessage( &uMsg );
      }
      else
      {
        drawScene(); //process regardless of focus state
      }
    } while( uMsg.message != WM_QUIT ); //application has not requested to terminate

    shutDown();
    return uMsg.wParam;
  }*/

#endif

#ifdef LINUX
  int main(int argc, char **argv)
  {
    #ifdef FREETYPE
      font = new FTGLTextureFont(font_data, sizeof(font_data));
      //font = new FTGLTextureFont("Fontin-Regular.otf");
    #endif
    
    XVisualInfo  *vi;
    Colormap    cmap;
    XSetWindowAttributes swa;
    GLXContext    cx;
    XEvent      event;
    int       dummy;
    
    dpy = XOpenDisplay(getenv ("DISPLAY"));
    if (dpy == NULL){
      fprintf(stderr, "could not open display\n");
      exit(1);
    }

    if(!glXQueryExtension(dpy, &dummy, &dummy)){
      fprintf(stderr, "could not open display");
      exit(1);
    }
    
    vi = glXChooseVisual(dpy, DefaultScreen(dpy), attributes);
    if (vi == NULL) {
      fprintf(stderr, "could not get visual\n");
      exit(1);
    }

    cx = glXCreateContext(dpy, vi,  None, GL_TRUE);
    if (cx == NULL) {
      fprintf(stderr, "could not create rendering context\n");
      exit(1);
    }

    cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone);
    swa.colormap = cmap;
    swa.border_pixel = 0;
    swa.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask;

    glwin = XCreateWindow(dpy, RootWindow(dpy, vi->screen), 1, 1, WIDTH, HEIGHT,   1, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa);

    XSetStandardProperties(dpy, glwin, "Lovely!", "Lovely!", None, argv, argc, NULL);

    glXMakeCurrent(dpy, glwin, cx);

    XMapWindow(dpy, glwin);
    
    for (int i = 0 ; i < num_schedules ; i++)
    {
      if (schedules[i].start_time < services[schedules[i].service_id-1].start && schedules[i].start_time != 0) services[schedules[i].service_id-1].start = schedules[i].start_time;
      if (schedules[i].end_time > services[schedules[i].service_id-1].end && schedules[i].end_time != 0) services[schedules[i].service_id-1].end = schedules[i].end_time;
    }
    
    XEvent xev;
    Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
    Atom fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);

    memset(&xev, 0, sizeof(xev));
    xev.type = ClientMessage;
    xev.xclient.window = glwin;
    xev.xclient.message_type = wm_state;
    xev.xclient.format = 32;
    xev.xclient.data.l[0] = 1;
    xev.xclient.data.l[1] = fullscreen;
    xev.xclient.data.l[2] = 0;

    XSendEvent(dpy, DefaultRootWindow(dpy), false, SubstructureNotifyMask, &xev);
    
    initializeGL();
    resize(WIDTH, HEIGHT);
    
    glEnable(GL_TEXTURE_2D);
    glShadeModel(GL_SMOOTH);
    glClearDepth(1.0f);
    glDisable(GL_DEPTH_TEST);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
    glEnable(GL_LINE_SMOOTH);
    shape_dl = glGenLists(1);
    glNewList(shape_dl, GL_COMPILE);
      for (int i = 0 ; i < num_shapes ; i++)
      {
        glBegin(GL_LINE_STRIP);
        for (int j = 0 ; j < shapes[i].size ; j++)
          glVertex3f((shapes[i].points + j)->y, (shapes[i].points + j)->x, 0);
        glEnd();
      }
    glEndList();
    
    while (1)
    {
      KeySym key;
      
      while (XPending(dpy))
      {
        XNextEvent(dpy, &event);
        switch (event.type)
        {
          case KeyPress: 
            XLookupString((XKeyEvent *)&event, NULL, 0, &key, NULL);
            switch (key) {
              case XK_Left: 
                x_offset -= 50;
                break;
              case XK_Right: 
                x_offset += 50;
                break;
              case XK_Up: 
                y_offset -= 50;
                break;
              case XK_Down: 
                y_offset += 50;
                break;
              case XK_equal:
                zoom *= 1.1;
                break;
              case XK_minus:
                zoom /= 1.1;
                break;
              case XK_r:
                displaymode = 'r';
                break;
              case XK_f:
                displaymode = 'f';
                break;
              case XK_q:
                exit(1);
                break;
            }
            break;
          case ConfigureNotify: 
            resize(event.xconfigure.width, event.xconfigure.height);
            break;
        }
      }
      drawScene();
    }
  }
#endif

